// RingBuffer.cop - ring (round robin) buffer implementation

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "RingBuffer.h"

// Data Ring buffer constructor
RingBuffer::RingBuffer(int sizeBytes)
{
	m_data = new unsigned char[sizeBytes];
	memset(m_data, 0, sizeBytes );
	m_size = sizeBytes;
	m_readPtr = 0;
	m_writePtr = 0;
	m_writeBytesAvail = sizeBytes;
	m_writeMutexCount = 0;
	m_readMutexCount = 0;

	// Initialize ring buffer mutex
	pthread_mutex_init(&m_ring_buffer_mutex, NULL);
}

// Data Ring buffer destructor
RingBuffer::~RingBuffer()
{
	// Delete ring buffer mutex
	pthread_mutex_destroy(&m_ring_buffer_mutex);

	// Delete ring buffer memory allocation
	delete[] m_data;
}

// Set all data to 0 and flag buffer as empty.
bool RingBuffer::Empty(void)
{
	memset(m_data, 0, m_size);
	m_readPtr = 0;
	m_writePtr = 0;
	m_writeBytesAvail = m_size;
	return true;
}

// Write to the ring buffer and reposition buffer pointers
int RingBuffer::Write(unsigned char *dataPtr, int numBytes)
{
	// If there's nothing to write or no room available, we can't write anything.
	if (dataPtr == NULL || numBytes <= 0) { // || m_writeBytesAvail == 0) {
		return 0;
	}

	// Lock access to ring buffer
	pthread_mutex_lock(&m_ring_buffer_mutex);
#if _DEBUG
	printf("Write Mutex lock Counter = %d\n", ++m_writeMutexCount);
#endif

	// Simultaneously keep track of how many bytes we've written
	// and our position in the incoming buffer
	if (numBytes > m_size - m_writePtr) {
		int len = m_size - m_writePtr;
		memcpy(m_data + m_writePtr, dataPtr, len);
		memcpy(m_data, dataPtr + len, numBytes - len);
	} else {
		memcpy(m_data + m_writePtr, dataPtr, numBytes);
	}

	m_writePtr = (m_writePtr + numBytes) % m_size;
	m_writeBytesAvail -= numBytes;

	// Release access to ring buffer
 	pthread_mutex_unlock(&m_ring_buffer_mutex);
#if _DEBUG
	printf("Write Mutex lock Counter = %d\n", --m_writeMutexCount);
#endif

	return numBytes;
}

// Read from the ring buffer and reposition buffer pointers
int RingBuffer::Read(unsigned char *dataPtr, int numBytes)
{
	// If there's nothing to read or no data available, then we can't read anything.
	if (dataPtr == NULL || numBytes <= 0) { // || m_writeBytesAvail < numBytes) {
		return 0;
	}

	// Lock access to ring buffer
	pthread_mutex_lock(&m_ring_buffer_mutex);
#if _DEBUG
	printf("Read Mutex lock Counter = %d\n", ++m_readMutexCount);
#endif

	// Simultaneously keep track of how many bytes we've read
	// and our position in the outgoing buffer
	if (numBytes > m_size - m_readPtr){
		int len = m_size - m_readPtr;
		memcpy(dataPtr, m_data + m_readPtr, len);
		memcpy(dataPtr+len, m_data, numBytes - len);
	} else {
		memcpy(dataPtr, m_data + m_readPtr, numBytes);
	}

	m_readPtr = (m_readPtr + numBytes) % m_size;
	m_writeBytesAvail += numBytes;

	// Release access to ring buffer
 	pthread_mutex_unlock(&m_ring_buffer_mutex);

#if _DEBUG
	printf("Data write %d bytes to dataPtr %x", numBytes, (int)dataPtr);
	printf("Read Mutex unlock Counter = %d\n", --m_readMutexCount);
#endif

	return (numBytes);
}

// Lock and show the current ring buffer contents
void RingBuffer::ShowBuffer()
{
	// Lock access to ring buffer
	pthread_mutex_lock(&m_ring_buffer_mutex);

	fprintf(stderr, "\nRing Buffer:0x%x  size:%d  write avail:%d  read avail:%d\n\n",
			(int)this, GetSize(), GetWriteAvail(), GetReadAvail());

	static int byteindex = 0;
	for (int i = 0; i < 256; i++) {
		fprintf(stderr, "%02x ", (unsigned int)(*(m_data + byteindex++) && 0xFF));
		if (!(byteindex % 32)) {
			fprintf(stderr, "\n");
		}
	}

	// Release access to ring buffer
 	pthread_mutex_unlock(&m_ring_buffer_mutex);
}

// Get size of ring buffer
int RingBuffer::GetSize(void)
{
	return m_size;
}

// Get the number of ring buffer bytes available to write
int RingBuffer::GetWriteAvail(void)
{
	return m_writeBytesAvail;
}

// Get the number of ring buffer bytes available to read
int RingBuffer::GetReadAvail(void)
{
	return m_size - m_writeBytesAvail;
}
